﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Draco.OWF.DataBinding;
using System.Reflection;
using Draco.OWF.Acticity;

namespace Draco.OWF.Repository.Serialize
{
    public class XmlSerializeProvider : ISerializeProvider
    {
        protected XmlDocument m_xDoc = new XmlDocument();
        public XmlSerializeProvider()
        {
        }
        public virtual void PersistentWorkflowProcess(IProcess process, IPackageHeader header)
        {
            XmlDeclaration Declare = m_xDoc.CreateXmlDeclaration("1.0", "UTF-8", "");
            m_xDoc.AppendChild(Declare);
            XmlElement root = m_xDoc.CreateElement("Package");
            m_xDoc.AppendChild(root);
            PersistentWorkflowProcess(process, root, m_xDoc);
            if (header != null)
            {
                //at last
                header.Serialize(root);
            }
        }

        protected virtual void PersistentWorkflowProcess(IProcess processes, XmlElement parent, XmlDocument xDoc)
        {
            XmlElement xmlProcess = xDoc.CreateElement("WorkflowProcess");
            parent.AppendChild(xmlProcess);

            XmlAttribute attrId = xDoc.CreateAttribute("Id");
            attrId.Value = processes.Id;
            xmlProcess.Attributes.Append(attrId);

            XmlAttribute attrName = xDoc.CreateAttribute("Name");
            attrName.Value = processes.Name;
            xmlProcess.Attributes.Append(attrName);

            #region Header
            XmlElement xmlProcessHeader = xDoc.CreateElement("ProcessHeader");
            xmlProcess.AppendChild(xmlProcessHeader);

            XmlElement xmlDisplayName = xDoc.CreateElement("DisplayName");
            xmlDisplayName.InnerText = processes.DisplayName;
            xmlProcessHeader.AppendChild(xmlDisplayName);
            XmlElement xmlDescription = xDoc.CreateElement("Description");
            xmlDescription.InnerText = processes.Description;
            xmlProcessHeader.AppendChild(xmlDescription);
            XmlElement xmlAuthor = xDoc.CreateElement("Author");
            xmlAuthor.InnerText = processes.Creator;
            xmlProcessHeader.AppendChild(xmlAuthor);
            XmlElement xmlCreatDate = xDoc.CreateElement("CreatDate");
            xmlCreatDate.InnerText = processes.CreateDate.ToString("yyyy-MM-dd HH:mm:ss");
            xmlProcessHeader.AppendChild(xmlCreatDate);
            XmlElement xmlVersion = xDoc.CreateElement("Version");
            xmlVersion.InnerText = processes.Version;
            xmlProcessHeader.AppendChild(xmlVersion);
            #endregion

            XmlElement xmlProcessRedefinableHeader = xDoc.CreateElement("RedefinableHeader");
            xmlProcess.AppendChild(xmlProcessRedefinableHeader);

            //Activities
            XmlElement xmlProcessActivities = xDoc.CreateElement("Activities");
            foreach (var ac in processes.Activities)
            {
                PersistentActivity(ac, xmlProcessActivities, xDoc);
            }
            xmlProcess.AppendChild(xmlProcessActivities);

            //Transitions
            XmlElement xmlProcessTransitions = xDoc.CreateElement("Transitions");
            foreach (var route in processes.Routes)
            {
                PersistentTransition(route, xmlProcessTransitions, xDoc);
            }
            xmlProcess.AppendChild(xmlProcessTransitions);
        }

        protected virtual void PersistentActivity(IActivity activity, XmlElement parent, XmlDocument xDoc)
        {
            XmlElement xmlActivity = xDoc.CreateElement("Activity");
            parent.AppendChild(xmlActivity);

            XmlAttribute attrId = xDoc.CreateAttribute("Id");
            attrId.Value = activity.Id;
            xmlActivity.Attributes.Append(attrId);

            XmlAttribute attrName = xDoc.CreateAttribute("Name");
            attrName.Value = activity.Name;
            xmlActivity.Attributes.Append(attrName);

            XmlAttribute attrType = xDoc.CreateAttribute("Type");
            attrType.Value = activity.ActivityType;
            xmlActivity.Attributes.Append(attrType);

            XmlElement xmlConfiguration = xDoc.CreateElement("Configuration");
            activity.SerializeConfiguration(xmlConfiguration);
            xmlActivity.AppendChild(xmlConfiguration);

            XmlElement xmlExtendedAttributes = xDoc.CreateElement("ExtendedAttributes");
            PropertyInfo[] properties = activity.GetType().GetProperties();
            foreach (PropertyInfo prop in properties)
            {
                Object[] attr = prop.GetCustomAttributes(typeof(ExtendedPropertyAttribute),true);
                if (attr != null && attr.Length > 0)
                {
                    XmlElement xmlExtended = xDoc.CreateElement(prop.Name);
                    xmlExtended.InnerText = Convert.ToString(prop.GetValue(activity,null));
                    xmlExtendedAttributes.AppendChild(xmlExtended);
                }
            }
            xmlActivity.AppendChild(xmlExtendedAttributes);

            //Inputs
            XmlElement xmlInputs = xDoc.CreateElement("Inputs");
            xmlActivity.AppendChild(xmlInputs);
            IBindingDataItemCollection inputs = activity.InputParameters;
            foreach (var input in inputs)
            {
                XmlElement xmlInput = xDoc.CreateElement("Input");
                xmlInput.InnerText = input.XQuery;
                xmlInputs.AppendChild(xmlInput);

                XmlAttribute attrPName = xDoc.CreateAttribute("Name");
                attrPName.InnerText = input.DataName;
                xmlInput.Attributes.Append(attrPName);

                XmlAttribute attrPType = xDoc.CreateAttribute("Type");
                attrPType.InnerText = input.DataType.ToString();
                xmlInput.Attributes.Append(attrPType);

                XmlAttribute attrNullable = xDoc.CreateAttribute("Nullable");
                attrNullable.InnerText = input.Nullable.ToString();
                xmlInput.Attributes.Append(attrNullable);
            }
            
            //Outputs
            XmlElement xmlOutputs = xDoc.CreateElement("Outputs");
            xmlActivity.AppendChild(xmlOutputs);
            IBindingDataItemCollection Outputs = activity.OutputParameters;
            foreach (var Output in Outputs)
            {
                XmlElement xmlInput = xDoc.CreateElement("Output");
                xmlOutputs.AppendChild(xmlInput);

                XmlAttribute attrPName = xDoc.CreateAttribute("Name");
                attrPName.InnerText = Output.DataName;
                xmlOutputs.Attributes.Append(attrPName);

                XmlAttribute attrPType = xDoc.CreateAttribute("Type");
                attrPType.InnerText = Output.DataType.ToString();
                xmlOutputs.Attributes.Append(attrPType);

                XmlAttribute attrNullable = xDoc.CreateAttribute("Nullable");
                attrNullable.InnerText = Output.Nullable.ToString();
                xmlOutputs.Attributes.Append(attrNullable);
            }

            //Render
            XmlElement xmlRender = xDoc.CreateElement("Render");
            xmlActivity.AppendChild(xmlRender);
        }

        protected virtual void PersistentTransition(IRoute route, XmlElement parent, XmlDocument xDoc)
        {
            XmlElement xmlTransition = xDoc.CreateElement("Transition");
            parent.AppendChild(xmlTransition);

            XmlAttribute attrId = xDoc.CreateAttribute("Id");
            attrId.Value = route.Id;
            xmlTransition.Attributes.Append(attrId);

            XmlAttribute attrFrom = xDoc.CreateAttribute("From");
            attrFrom.Value = route.FromId;
            xmlTransition.Attributes.Append(attrFrom);

            XmlAttribute attrTo = xDoc.CreateAttribute("To");
            attrTo.Value = route.ToId;
            xmlTransition.Attributes.Append(attrTo);

            XmlAttribute attrtype = xDoc.CreateAttribute("Type");
            attrtype.Value = route.TransitionType;
            xmlTransition.Attributes.Append(attrtype);

            XmlElement xmlRender = xDoc.CreateElement("Render");
            xmlTransition.AppendChild(xmlRender);
        }

        public XmlDocument Document { get { return this.m_xDoc; } }
        public void Save(String path)
        {
            Document.Save(path);
        }
    }
}
